package com.sd365.gateway.authentication.service.impl;

import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.sd365.common.util.TokenUtil;
import com.sd365.gateway.authentication.service.AuthenticationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * @Class AuthenticationServiceImpl
 * @Description abel.zhan 2022-10-12 重构认证代码
 *  需要实现  AuthenticationService  按系统开发指引流程实现认证逻辑
 * @Author Administrator
 * @Date 2022-10-12  20:08
 * @version 1.0.0
 */
@Slf4j
@Service
@Valid
public class AuthenticationServiceImpl implements AuthenticationService {
    /**
     * 定义了认证接口的URL
     */
    public static final  String AUTH_URL = "http://sd365-permission-center/permission/centre/v1/user/auth?code=%s&account=%s&password=%s";
    /**
     * RedisTemplateConfig 中生成
     */
    @Resource(name = "tokenRedisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * CurrentServiceTemplateBeanConfig.java中定义
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 默认设置三天过期 单位毫秒 1000 * 60 * 60 * 24 * 3 = 86400
     */
    @Value("${jwt.period}")
    private static final Long period = 86400000L;
    /**
     * 一天
     */
    private static final Long ONE_DAY = 86400000L;
    private static final String USER_TOKEN_KEY = "user:token:";

    /**
     * UserVO 属性值 常量
     */
    private static final String ROLE_IDS = "roleIds";
    private static final String NAME = "name";
    private static final String ID = "id";
    private static final String TENANT_ID = "tenantId";
    private static final String COMPANY_ID = "companyId";
    private static final String ORG_ID = "orgId";

    /*
    默认值
     */
    private static final String DEFAULT_VALUE = "-1";

    /**
     * 用于验证 并且返回token
     * @param code 账户
     * @param account 租户id
     * @param password 密码
     * @return jwt token
     */
    @Override
    public String getToken(@NotNull String code,@NotNull String account,@NotNull String password) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        log.info("HttpServletRequest request ====>{}", request);
        // 用户中心返回的CommonResponse 包含 UserVO对象
        // 由于restTemplate请求会导致header丢失，所以手动添加header
        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.USER_AGENT,request.getHeader("USER-AGENT"));
        HttpEntity entity = new HttpEntity<>(null, headers);
        /*
            使用RestTemplate调用
         1. 在用户中心判断用用户是否存在
         2. if 用户存在 调用createToken方法生成 jwt token
         */
        return createToken(code, account, Objects.requireNonNull(
                restTemplate.exchange(String.format(AUTH_URL, code, account, password),
                        HttpMethod.GET, entity, HashMap.class).getBody()));
    }

    /**
     * 生成jwt token的算法
     * @param code 账号
     * @param account 租户id
     * @param loginResponse 返回的UserVO
     * @return jwt token
     */
    private String createToken(String code, String account,@NotNull HashMap loginResponse) {

        // 取得CommonResponse封装的UserVO
        HashMap<String,Object> userBody = (HashMap<String, Object>) loginResponse.get("body");
        /*
           用户中心返回new UserVO  ，UserVO的code值 1 代表认证通过 0 代表租户错误 2 代码账号或者密码错误
         */
        Integer businessCode=(Integer) userBody.get("code");
        if (businessCode != HttpStatus.HTTP_OK) {
            return new JSONObject(loginResponse).toJSONString();
        }
        /*
        这些是 jwt token 需要存储的元素
         */
        ArrayList<Object> roleIds = new ArrayList<>();
        roleIds.add((userBody.getOrDefault(ROLE_IDS,DEFAULT_VALUE)));
        String userName = (String) userBody.getOrDefault(NAME, DEFAULT_VALUE);
        Long userId = Long.parseLong((String) userBody.getOrDefault(ID,DEFAULT_VALUE));
        Long tenantId = Long.parseLong((String) userBody.getOrDefault(TENANT_ID, DEFAULT_VALUE));
        Long companyId = Long.parseLong((String) userBody.getOrDefault(COMPANY_ID, DEFAULT_VALUE));
        Long orgId = Long.parseLong((String) userBody.getOrDefault(ORG_ID, DEFAULT_VALUE));
        // 根据这些元素 生成 jwt token
        String encoderToken = JwtUtil.build(code,account,roleIds,tenantId,userId,companyId,orgId,userName);
        HashMap responseData = (HashMap) userBody.get("data");
        responseData.put("token",encoderToken);
        // 将token userId 存入Redis
        inputRedis(userId,encoderToken);
        // 返回的报文带了JWT规范加密后的token
        return new JSONObject(loginResponse).toJSONString();
    }

    /**
      将token userId 存入Redis
     */
    private void inputRedis(@NotNull Long userId,@NotNull String encoderToken) {
        try {
            redisTemplate.opsForValue().set(userId.toString(), "userId:" + userId.toString(), 1L, TimeUnit.DAYS);
            stringRedisTemplate.opsForValue().set(USER_TOKEN_KEY + userId, encoderToken , period + ONE_DAY, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
